home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / Pascal / Code Resources / Eclectic CDEFs / Celsius CDEF Folder / CelsiusCDEF.p < prev    next >
Text File  |  1997-03-05  |  30KB  |  770 lines

  1. {    CelsiusCDEF    }
  2. {}
  3. {    Control definition for the CelsiusCDEF control    }
  4. {}
  5. {    Copyright © Sebastiano Pilla 1996    }
  6. {    <mailto:case@tvol.it>    }
  7.  
  8. {    Control definition procedure for implementing a progress bar similar to    that used by the Finder.    }
  9. {    Additionally, by setting the appropriate variation codes, it is possible to:    }
  10. {    1) obtain a 'barber pole' effect (indefinite progress bar)    }
  11. {    2) ignore the colors in any control color table (either default or custom) and use always the standard colors    }
  12. {    3) never dim the control, thus drawing always in the 'active' state    }
  13. {    4) draw a 3D-like inset effect    }
  14.  
  15. {    Credits    }
  16. {}
  17. {    Chris Larson for FinderProgressBar    }
  18. {    Andrew Regan for ProgressBar CDEF    }
  19. {    Jim Stout for Jim's CDEF    }
  20. {    Harold Ekstrom for Slider CDEF    }
  21.  
  22. unit CelsiusCDEF;
  23.  
  24.  
  25. interface
  26.  
  27.  
  28.     uses
  29.         Windows, Palettes, CDEFUtils, CalculateBarBoundaryIntf;
  30.  
  31.  
  32.     function Main (inVarCode: SInt16;
  33.                                     inControlHdl: ControlHandle;
  34.                                     inMessage: ControlDefProcMessage;
  35.                                     inParam: SInt32): SInt32;
  36.  
  37.  
  38. implementation
  39.  
  40.  
  41.     const
  42.         kInCelsiusControlPart = 50;                { Value returned for the testCntl message }
  43.  
  44.         kBarberPoleVarCodeMask = $1;            { Mask for obtaining the barber pole animation }
  45.         kUseStdColorsVarCodeMask = $2;            { Mask for drawing the bar with the standard dark gray-steel blue colors }
  46.         kNeverDimControlVarCodeMask = $4;        { Mask for drawing the bar always in the active state }
  47.         kInsetBorderVarCodeMask = $8;            { Mask for drawing an inset border around the bar }
  48.  
  49.         kDeviceLoopFlags = 0;                        { Flags passed to DeviceLoop }
  50.  
  51.         kMinimumColorDepth = 4;                    { Minimum depth for drawing in color, in bits per pixel }
  52.         kMinimum3DDepth = 8;                    { Minimum depth for drawing the inset border, in bits per pixel }
  53.  
  54.         kBarberStripeWidth = 8;                    { Width of barber pole strips }
  55.         kFPBFrameCount = 16;                    { Number of frames in the barber pole animation loop }
  56.  
  57.  
  58.     type
  59.         CelsiusCDEFDataHandle = ^CelsiusCDEFDataPtr;
  60.         CelsiusCDEFDataPtr = ^CelsiusCDEFData;
  61.         CelsiusCDEFData = record
  62.                 fDrawControlUPP: DeviceLoopDrawingUPP;    { Pointer to drawing routine }
  63.                 fBlitControlUPP: DeviceLoopDrawingUPP;    { Pointer to blitting routine }
  64.                 fOffscreenWorldPtr: GWorldPtr;                { Pointer to offscreen world }
  65.                 fBlackPattern: Pattern;                        { Standard black pattern }
  66.                 fWhitePattern: Pattern;                        { Standard white pattern }
  67.                 fDitherPattern: Pattern;                        { 50% black-50% white pattern used for dimming }
  68.                 fControlOwnerForeColor: RGBColor;            { Foreground color of the control's window }
  69.                 fControlOwnerContentColor: RGBColor;        { Content color of the control's window }
  70.                 fBlackColor: RGBColor;                        { ($0000, $0000, $0000) black color }
  71.                 fWhiteColor: RGBColor;                        { ($FFFF, $FFFF, $FFFF) white color }
  72.                 fDarkGrayColor: RGBColor;                    { ($4000, $4000, $4000) dark gray ('done' part) color }
  73.                 fSteelBlueColor: RGBColor;                    { ($CCCC, $CCCC, $FFFF) steel blue ('to do' part) color }
  74.                 fDimGrayColor: RGBColor;                    { ($7FFF, $7FFF, $7FFF) gray color for dimming }
  75.                 fChiselGrayColor: RGBColor;                    { ($AAAA, $AAAA, $AAAA) chisel color as per develop 15 }
  76.                 fVariationCode: SInt16;                        { VarCode of current control (no other way of passing it to the draw routine) }
  77.                 fDepth: UInt16;                                { Saved depth state, for updating the offscreen world }
  78.                 fOffscreenDrawAvailable: Boolean;            { True if we can draw in the offscreen world, false otherwise }
  79.             end;
  80.  
  81.  
  82. {    DrawInsetBorder    }
  83. {}
  84. {    Draws a 3D-like inset border in the specified rectangle if the content color of the control's window    }
  85. {    is the standard light gray ($EEEE, $EEEE, $EEEE), as defined in develop 15    }
  86. {}
  87. {    Entry:    inControlBounds = rectangle enclosing the control    }
  88. {            inWinBackColor = content color of the control's window    }
  89. {            inControlDataHdl = handle to border colors    }
  90.     procedure Draw3DInsetEffect (inControlBounds: Rect;
  91.                                     inWinBackColor: RGBColor;
  92.                                     inControlDataHdl: CelsiusCDEFDataHandle);
  93.     begin
  94.  
  95.     { Draw the 3D border only if the window's content color is ($EEEE, $EEEE, $EEEE) }
  96.         if EqualRGBColorComponents(inWinBackColor, kLightGrayRGBComp) then
  97.             begin
  98.                 RGBBackColor(inWinBackColor);
  99.  
  100.         { Draw the top and left margins with the gray color and the bottom and right margins with the white }
  101.         { color to achieve an 'inset' effect }
  102.                 RGBForeColor(inControlDataHdl^^.fWhiteColor);
  103.                 MoveTo(inControlBounds.left + 1, inControlBounds.bottom - 1);
  104.                 LineTo(inControlBounds.right - 1, inControlBounds.bottom - 1);
  105.                 LineTo(inControlBounds.right - 1, inControlBounds.top);
  106.                 RGBForeColor(inControlDataHdl^^.fChiselGrayColor);
  107.                 MoveTo(inControlBounds.left, inControlBounds.bottom - 1);
  108.                 LineTo(inControlBounds.left, inControlBounds.top);
  109.                 LineTo(inControlBounds.right - 1, inControlBounds.top);
  110.             end;
  111.     end;
  112.  
  113.  
  114. {    DrawBarberPoleBar    }
  115. {}
  116. {    Draws the 'barber pole' animation effect    }
  117. {}
  118. {    Entry:    inControlBounds = rectangle for drawing (prepared by the caller)    }
  119. {            inDarkColor = darker color for the diagonal stripes    }
  120. {            inLightColor = lighter color for the diagonal stripes    }
  121. {            inWinBackColor = background color of the control's window    }
  122. {            inControlHdl = handle to current control, to get the value    }
  123. {            inDepth = depth of current device    }
  124. {            inDitherContentFlag = TRUE if the bar content should be dithered, false otherwise    }
  125.     procedure DrawBarberPoleBar (inControlBounds: Rect;
  126.                                     inDarkColor, inLightColor, inWinBackColor: RGBColor;
  127.                                     inControlHdl: ControlHandle;
  128.                                     inDepth: UInt16;
  129.                                     inDitherContentFlag: Boolean);
  130.         var
  131.             i, height, frameNum: SInt16;
  132.     begin
  133.  
  134.     { Set the pen size for the diagonal stripes }
  135.         PenSize(kBarberStripeWidth, 1);
  136.  
  137.     { Setup the colors for drawing }
  138.         if inDepth >= kMinimumColorDepth then
  139.             begin
  140.                 RGBForeColor(inDarkColor);
  141.                 RGBBackColor(inLightColor);
  142.             end
  143.         else
  144.             begin
  145.                 ForeColor(blackColor);
  146.                 BackColor(whiteColor);
  147.             end;
  148.  
  149.     { Compute the horizontal starting point for drawing. Note that each drawing loop draws one }
  150.     { stripe of each color (with gray drawn first). This starting point is set far enough to the }
  151.     { left to make the first set of stripes drawn hit the lower left corner of the bar }
  152.         height := inControlBounds.bottom - inControlBounds.top;
  153.         frameNum := inControlHdl^^.contrlValue mod kFPBFrameCount;
  154.         i := height + frameNum + (2 * kBarberStripeWidth) - 2;
  155.         i := i div (2 * kBarberStripeWidth);
  156.         i := inControlBounds.left - (i * 2 * kBarberStripeWidth) + frameNum;
  157.  
  158.     { Now back bias the starting location to account for the first increment }
  159.         i := i - kBarberStripeWidth;
  160.  
  161.     { Until we would start drawing to the right of the bar, draw a pair of lines, slanting to the }
  162.     { right, first of the two in the darker color, second in the lighter color }
  163.         while i < inControlBounds.right do
  164.             begin
  165.                 i := i + kBarberStripeWidth;
  166.                 PenMode(patCopy);
  167.                 MoveTo(i, inControlBounds.top);
  168.                 LineTo((i + height), (inControlBounds.bottom - 1));
  169.                 i := i + kBarberStripeWidth;
  170.                 PenMode(patBic);
  171.                 MoveTo(i, inControlBounds.top);
  172.                 LineTo((i + height), (inControlBounds.bottom - 1));
  173.             end;
  174.  
  175.     { If this control should be dimmed, paint the control's rectangle with the dithering pattern }
  176.         if inDitherContentFlag then
  177.             begin
  178.                 if inDepth >= kMinimumColorDepth then
  179.                     RGBBackColor(inWinBackColor);
  180.                 PenMode(notPatBic);
  181.                 PenPat(CelsiusCDEFDataHandle(inControlHdl^^.contrlData)^^.fDitherPattern);
  182.                 PaintRect(inControlBounds);
  183.             end;
  184.     end;
  185.  
  186.  
  187. {    DrawNormalBar    }
  188. {}
  189. {    Draws the normal done-to do progress bar using the supplied colors    }
  190. {}
  191. {    Entry:    inControlBounds = rectangle for drawing (prepared by the caller)    }
  192. {            inDarkColor = darker color for the 'done' part    }
  193. {            inLightColor = lighter color for the 'to do'    }
  194. {            inWinBackColor = background color of the control's window    }
  195. {            inControlHdl = handle to current control, to get the value    }
  196. {            inDepth = depth of current device    }
  197. {            inDitherContentFlag = TRUE if the bar content should be dithered, false otherwise    }
  198.     procedure DrawNormalBar (inControlBounds: Rect;
  199.                                     inDarkColor, inLightColor, inWinBackColor: RGBColor;
  200.                                     inControlHdl: ControlHandle;
  201.                                     inDepth: UInt16;
  202.                                     inDitherContentFlag: Boolean);
  203.         var
  204.             savedBounds: Rect;
  205.             savedRightBound: SInt16;
  206.     begin
  207.  
  208.     { To properly dither the bar later, we must save here the current control's bounds, because }
  209.     { they're altered by the following calls }
  210.         if inDitherContentFlag then
  211.             savedBounds := inControlBounds;
  212.  
  213.     { Save the current right border of the control's rect, because is modified by the CalculateBarBoundary call }
  214.         savedRightBound := inControlBounds.right;
  215.         inControlBounds.right := CalculateBarBoundary(inControlHdl, inControlBounds.left, inControlBounds.right);
  216.  
  217.     { Set up colors or pen pattern for the bar, then draw the bar  }
  218.         if inDepth >= kMinimumColorDepth then
  219.             begin
  220.                 RGBForeColor(inDarkColor);
  221.                 RGBBackColor(inWinBackColor);
  222.             end
  223.         else
  224.             PenPat(CelsiusCDEFDataHandle(inControlHdl^^.contrlData)^^.fBlackPattern);
  225.         PaintRect(inControlBounds);
  226.  
  227.     { Now set up the rectangle to draw the 'empty' space not yet filled by the bar }
  228.         inControlBounds.left := inControlBounds.right;
  229.         inControlBounds.right := savedRightBound;
  230.  
  231.     { Set up colors or pen pattern for the 'empty' space, then draw the space }
  232.         if inDepth >= kMinimumColorDepth then
  233.             begin
  234.                 RGBForeColor(inLightColor);
  235.                 RGBBackColor(inWinBackColor);
  236.             end
  237.         else
  238.             PenPat(CelsiusCDEFDataHandle(inControlHdl^^.contrlData)^^.fWhitePattern);
  239.         PaintRect(inControlBounds);
  240.  
  241.     { Dither the content if we're asked, using the previously saved rect }
  242.         if inDitherContentFlag then
  243.             begin
  244.                 if inDepth >= kMinimumColorDepth then
  245.                     RGBBackColor(inWinBackColor);
  246.                 PenMode(notPatBic);
  247.                 PenPat(CelsiusCDEFDataHandle(inControlHdl^^.contrlData)^^.fDitherPattern);
  248.                 PaintRect(savedBounds);
  249.             end;
  250.     end;
  251.  
  252.  
  253. {    DrawCelsiusControl    }
  254. {}
  255. {    DeviceLoop draw routine to draw the control in either the offscreen world or the control's port    }
  256. {}
  257. {    Entry:    inDepth = depth of current device    }
  258. {            inDeviceFlags = flags describing current device properties (unused)    }
  259. {            inTargetDevice = handle to current device    }
  260. {            inUserData = container for the control's handle }
  261.     procedure DrawCelsiusControl (inDepth: UInt16;
  262.                                     inDeviceFlags: SInt16;
  263.                                     inTargetDevice: GDHandle;
  264.                                     inUserData: SInt32);
  265.         var
  266.             controlBounds: Rect;
  267.             frameColor, darkColor, lightColor, winBackColor: RGBColor;
  268.             controlHdl: ControlHandle;
  269.             controlDataHdl: CelsiusCDEFDataHandle;
  270.             auxCtlHdl: AuxCtlHandle;
  271.             colorTabHdl: CCTabHandle;
  272.             varCode: SInt16;
  273.             hiliteState: UInt8;
  274.             useStdColorsFlag, dimFlag: Boolean;
  275.     begin
  276.  
  277.     { Get the control handle, the data handle and the control's hilite state }
  278.         controlHdl := ControlHandle(inUserData);
  279.         controlDataHdl := CelsiusCDEFDataHandle(controlHdl^^.contrlData);
  280.         hiliteState := controlHdl^^.contrlHilite;
  281.  
  282.     { Get the variation code and the control's rect (in local coordinates relative to the control's window) }
  283.         varCode := controlDataHdl^^.fVariationCode;
  284.         controlBounds := controlHdl^^.contrlRect;
  285.  
  286.     { Set the drawing port to either the offscreen world, if available, or the control's port }
  287.         if controlDataHdl^^.fOffscreenDrawAvailable then
  288.             SetGWorld(controlDataHdl^^.fOffscreenWorldPtr, nil)
  289.         else
  290.             SetGWorld(CGrafPtr(controlHdl^^.contrlOwner), nil);
  291.  
  292.     { Always normalize the pen before drawing, to avoid unwanted side effects }
  293.         PenNormal;
  294.  
  295.     { Dim the control only if the 'never dim' varCode is clear and if the control is inactive }
  296.         dimFlag := (BAND(varCode, kNeverDimControlVarCodeMask) = 0) & (hiliteState = kControlInactivePart);
  297.  
  298.     { Must we use the standard dark gray / steel blue colors? }
  299.         useStdColorsFlag := BAND(varCode, kUseStdColorsVarCodeMask) <> 0;
  300.  
  301.     { Set a well-known clipping; again, this helps avoiding surprises }
  302.         ClipRect(controlBounds);
  303.  
  304.     { Check if the current device is deep enough for drawing our colors; if so, either fetch the colors from the control's }
  305.     { data if we have to use the standard colors, or get them from the auxiliary control record. }
  306.     { Then draw the inset border (if requested) and the frame }
  307.         if inDepth >= kMinimumColorDepth then
  308.             begin
  309.  
  310.         { Fetch the content color of the control's window that the BeginDraw routine stored for us }
  311.                 winBackColor := controlDataHdl^^.fControlOwnerContentColor;
  312.  
  313.         { Fetch the standard colors, stored at the control initialization in the control's data; also, pay attention to the }
  314.         { control's hilite state and fetch the correct frame color }
  315.                 if useStdColorsFlag then
  316.                     begin
  317.                         if dimFlag then
  318.                             frameColor := controlDataHdl^^.fDimGrayColor
  319.                         else
  320.                             frameColor := controlDataHdl^^.fBlackColor;
  321.                         darkColor := controlDataHdl^^.fDarkGrayColor;
  322.                         lightColor := controlDataHdl^^.fSteelBlueColor;
  323.                     end
  324.                 else
  325.                     begin
  326.  
  327.             { Retrieve the control's auxiliary record; this strange 'if ... then ;' statement discards the function result (we }
  328.             { don't care about it) }
  329.                         if GetAuxiliaryControlRecord(controlHdl, auxCtlHdl) then
  330.                             ;
  331.  
  332.             { Get the color table and lock it (necessary to avoid NIL dereferences) }
  333.                         colorTabHdl := auxCtlHdl^^.acCTable;
  334.                         HLock(Handle(colorTabHdl));
  335.  
  336.             { The control should be dimmed, so fetch the foreground color of the control's owner from the control's data and }
  337.             { call GetGray to obtain the best gray for this device; note that if GetGray fails we use our standard dimGray color }
  338.                         if dimFlag then
  339.                             begin
  340.                                 frameColor := controlDataHdl^^.fControlOwnerForeColor;
  341.                                 if not GetGray(inTargetDevice, winBackColor, frameColor) then
  342.                                     frameColor := controlDataHdl^^.fDimGrayColor;
  343.                             end
  344.                         else
  345.  
  346.                 { The control shouldn't be dimmed, so fetch the frame color from the associated color table }
  347.                             frameColor := colorTabHdl^^.ctTable[cFrameColor].rgb;
  348.  
  349.             { The lighter color is the body color, and the darker color is the text color in the control's color table }
  350.                         lightColor := colorTabHdl^^.ctTable[cBodyColor].rgb;
  351.                         darkColor := colorTabHdl^^.ctTable[cTextColor].rgb;
  352.  
  353.             { Unlock the color table since we're done with it }
  354.                         HUnlock(Handle(colorTabHdl));
  355.                     end;
  356.  
  357.         { Determine if the control can feature the 'inset' effect, and call Draw3DInsetEffect }
  358.                 if (inDepth >= kMinimum3DDepth) and (BAND(varCode, kInsetBorderVarCodeMask) <> 0) and (not dimFlag) then
  359.                     Draw3DInsetEffect(controlBounds, winBackColor, controlDataHdl)
  360.                 else
  361.  
  362.         { The 'inset' effect cannot be displayed, so frame the control's rectangle with the content color of the control's }
  363.         { window. This maintains consistency: the rectangle in which we draw the actual progress bar (or barber }
  364.         { pole) should always keep the same size, regardless of the presence of the inset border. Letting the bar height }
  365.         { float causes very unpleasant effects if the user changes the screen depth between 2 subsequent drawings. }
  366.                     begin
  367.                         RGBForeColor(winBackColor);
  368.                         RGBBackColor(winBackColor);
  369.                         FrameRect(controlBounds);
  370.                     end;
  371.             end
  372.         else
  373.  
  374.     { Black-&-white (or 4-colors) device: frame the control's rectangle with a white pattern. }
  375.             begin
  376.                 PenPat(controlDataHdl^^.fWhitePattern);
  377.                 FrameRect(controlBounds);
  378.             end;
  379.  
  380.     { Inset the control rect by 1 pixel to avoid overwriting the inset border, and for consistency in the case }
  381.     { where the border is absent. See above for a more detailed explanation. }
  382.         InsetRect(controlBounds, 1, 1);
  383.  
  384.     { Draw the control's frame. }
  385.         if inDepth >= kMinimumColorDepth then
  386.             begin
  387.                 RGBForeColor(frameColor);
  388.                 RGBBackColor(winBackColor);
  389.             end
  390.         else if inDepth < kMinimumColorDepth then
  391.             if dimFlag then
  392.                 PenPat(controlDataHdl^^.fDitherPattern)
  393.             else
  394.                 PenPat(controlDataHdl^^.fBlackPattern);
  395.         FrameRect(controlBounds);
  396.  
  397.     { Inset the control rect by 1 pixel to avoid drawing over the frame; for maximum security, clip everything }
  398.     { outside this newly inset rectangle }
  399.         InsetRect(controlBounds, 1, 1);
  400.         ClipRect(controlBounds);
  401.  
  402.     { Determine if we must draw the 'barber pole' animation or the regular bar and draw }
  403.         if BAND(varCode, kBarberPoleVarCodeMask) <> 0 then
  404.             DrawBarberPoleBar(controlBounds, darkColor, lightColor, winBackColor, controlHdl, inDepth, dimFlag)
  405.         else
  406.             DrawNormalBar(controlBounds, darkColor, lightColor, winBackColor, controlHdl, inDepth, dimFlag);
  407.     end;
  408.  
  409.  
  410. {    BlitCelsiusControl    }
  411. {}
  412. {    DeviceLoop draw routine to copy the control drawing from the offscreen world to the control's port    }
  413. {}
  414. {    Entry:    inDepth = depth of current device    }
  415. {            inDeviceFlags = flags describing current device properties (unused)    }
  416. {            inTargetDevice = handle to current device (unused)    }
  417. {            inUserData = container for the control's handle }
  418.     procedure BlitCelsiusControl (inDepth: UInt16;
  419.                                     inDeviceFlags: SInt16;
  420.                                     inTargetDevice: GDHandle;
  421.                                     inUserData: SInt32);
  422.         var
  423.             controlBounds: Rect;
  424.             controlDataHdl: CelsiusCDEFDataHandle;
  425.             offWorldPtr: GWorldPtr;
  426.             controlPort: CGrafPtr;
  427.     begin
  428.  
  429.     { Get the control's data, the control's port and the control's rect }
  430.         controlDataHdl := CelsiusCDEFDataHandle(ControlHandle(inUserData)^^.contrlData);
  431.         controlPort := CGrafPtr(ControlHandle(inUserData)^^.contrlOwner);
  432.         controlBounds := ControlHandle(inUserData)^^.contrlRect;
  433.  
  434.     { Proceed only if the offscreen world is available }
  435.         if controlDataHdl^^.fOffscreenDrawAvailable then
  436.             begin
  437.                 offWorldPtr := controlDataHdl^^.fOffscreenWorldPtr;
  438.  
  439.         { The offscreen world's pixMap has already been locked by the caller, so we set the port }
  440.         { to the control's owner, set the foreground color to black and background color to white to avoid colorization }
  441.         { by CopyBits and start blitting }
  442.                 SetGWorld(controlPort, nil);
  443.                 RGBForeColor(controlDataHdl^^.fBlackColor);
  444.                 RGBBackColor(controlDataHdl^^.fWhiteColor);
  445.                 CopyBits(GrafPtr(offWorldPtr)^.portBits, GrafPtr(controlPort)^.portBits, offWorldPtr^.portRect, controlBounds, srcCopy, nil);
  446.             end;
  447.     end;
  448.  
  449.  
  450. {    BeginDraw    }
  451. {}
  452. {    Responds to the drawCntl message by saving the current port, color, ecc. settings, by activating ours and    }
  453. {    by calling DeviceLoop to draw the control    }
  454. {}
  455. {    Entry:    inControlHdl = handle to current control    }
  456. {            inVarCode = variation code of current control    }
  457.     procedure BeginDraw (inControlHdl: ControlHandle;
  458.                                     inVarCode: SInt16);
  459.         var
  460.             controlBounds: Rect;
  461.             saveForeColor, saveBackColor: RGBColor;
  462.             savePen: PenState;
  463.             saveClip, controlRgn, drawingRgn: RgnHandle;
  464.             auxWinHdl: AuxWinHandle;
  465.             colorTabHdl: CTabHandle;
  466.             savePort, controlPort: CGrafPtr;
  467.             saveDevice: GDHandle;
  468.             controlDataHdl: CelsiusCDEFDataHandle;
  469.             ignoredFlags: GWorldFlags;
  470.             currDepth: UInt16;
  471.             offscreenDrawFlag: Boolean;
  472.     begin
  473.  
  474.     { Exit immediately if our custom data isn't available }
  475.         controlDataHdl := CelsiusCDEFDataHandle(inControlHdl^^.contrlData);
  476.         if controlDataHdl = nil then
  477.             Exit(BeginDraw);
  478.  
  479.     { Save current settings (port, clipping, colors, ecc.) }
  480.         GetGWorld(savePort, saveDevice);
  481.         controlPort := CGrafPtr(inControlHdl^^.contrlOwner);
  482.         SetGWorld(controlPort, nil);
  483.         GetForeColor(saveForeColor);
  484.         GetBackColor(saveBackColor);
  485.         GetPenState(savePen);
  486.  
  487.     { Now the current port is the control's port, and we intersect its clipping region with our control rectangle; if this }
  488.     { intersection is empty then exit without drawing anything. This indeed means that all our drawing would be clipped out }
  489.  
  490.     { Get the visRgn of the control's window, to pass it to DeviceLoop later }
  491.         drawingRgn := controlPort^.visRgn;
  492.  
  493.         controlBounds := inControlHdl^^.contrlRect;
  494.  
  495.     { Allocate 2 regions for our purposes }
  496.         controlRgn := NewRgn;
  497.         saveClip := NewRgn;
  498.  
  499.         if (saveClip <> nil) and (controlRgn <> nil) then
  500.             begin
  501.                 GetClip(saveClip);
  502.                 RectRgn(controlRgn, controlBounds);
  503.  
  504.         { Intersect the current clip region with the control's rectangle: if this intersection is empty then dispose of our }
  505.         { regions and exit }
  506.                 SectRgn(saveClip, controlRgn, controlRgn);
  507.                 if EmptyRgn(controlRgn) then
  508.                     begin
  509.                         DisposeRgn(saveClip);
  510.                         DisposeRgn(controlRgn);
  511.                         SetGWorld(savePort, saveDevice);
  512.                         Exit(BeginDraw);
  513.                     end
  514.                 else
  515.  
  516.         { All right: set the clip region to the previously calculated intersection and go ahead }
  517.                     SetClip(controlRgn);
  518.             end
  519.         else
  520.  
  521.     { Bad, bad: we don't have enough memory to allocate 2 regions, so exit }
  522.             begin
  523.                 SetGWorld(savePort, saveDevice);
  524.                 Exit(BeginDraw);
  525.             end;
  526.  
  527.     { Lock our data to avoid problems }
  528.         HLock(Handle(controlDataHdl));
  529.  
  530.     { Drawing with the 'cctb' colors requires informations about the foreground color of the control's window, so }
  531.     { store it into our data to avoid another couple of Get/SetGWorld calls later. }
  532.     { Possible improvement: storing this color is needed only if dimFlag = true, so we could calculate dimFlag in this }
  533.     { routine rather than in DrawCelsiusControl }
  534.         if BAND(inVarCode, kUseStdColorsVarCodeMask) = 0 then
  535.             controlDataHdl^^.fControlOwnerForeColor := saveForeColor;
  536.  
  537.     { Store into our data the content color of the control's window, because we will need it to frame the }
  538.     { control's bounds when not drawing the inset border }
  539.         if GetAuxWin(WindowPtr(controlPort), auxWinHdl) then
  540.             ;
  541.         colorTabHdl := auxWinHdl^^.awCTable;
  542.         HLock(Handle(colorTabHdl));
  543.         controlDataHdl^^.fControlOwnerContentColor := colorTabHdl^^.ctTable[wContentColor].rgb;
  544.         HUnlock(Handle(colorTabHdl));
  545.  
  546.     { If the offscreen world is available, update it if the depth changed between the last drawing and call }
  547.     { DeviceLoop 1) first to draw into it and 2) to blit from it to the control's port; otherwise call DeviceLoop }
  548.     { to do a simple draw in the control's window }
  549.         offscreenDrawFlag := (controlDataHdl^^.fOffscreenWorldPtr <> nil) & LockPixels(GetGWorldPixMap(controlDataHdl^^.fOffscreenWorldPtr));
  550.  
  551.     { Signal the availability of the offscreen world to the actual draw procedure }
  552.         controlDataHdl^^.fOffscreenDrawAvailable := offscreenDrawFlag;
  553.  
  554.         if offscreenDrawFlag then
  555.             begin
  556.  
  557.         { Update our offscreen world if the depth has changed, and store the new depth}
  558.                 currDepth := GetControlPortDepth(inControlHdl);
  559.                 if controlDataHdl^^.fDepth <> currDepth then
  560.                     begin
  561.                         ignoredFlags := UpdateGWorld(controlDataHdl^^.fOffscreenWorldPtr, kDeepestDeviceDepth, controlDataHdl^^.fOffscreenWorldPtr^.portRect, nil, nil, kOffWorldFlags);
  562.                         controlDataHdl^^.fDepth := currDepth;
  563.                     end;
  564.  
  565.         { Draw the control in the offscreen world }
  566.                 DeviceLoop(drawingRgn, controlDataHdl^^.fDrawControlUPP, SInt32(inControlHdl), kDeviceLoopFlags);
  567.  
  568.         { Copy the newly drawn control from the offscreen world to the control's port }
  569.                 DeviceLoop(drawingRgn, controlDataHdl^^.fBlitControlUPP, SInt32(inControlHdl), kDeviceLoopFlags);
  570.                 UnlockPixels(GetGWorldPixMap(controlDataHdl^^.fOffscreenWorldPtr));
  571.             end
  572.         else
  573.  
  574.     { The offscreen world is not accessible, so we draw in the control's port anyway }
  575.             DeviceLoop(drawingRgn, controlDataHdl^^.fDrawControlUPP, SInt32(inControlHdl), kDeviceLoopFlags);
  576.  
  577.     { Restore the previous settings, unlock our data and exit }
  578.         SetClip(saveClip);
  579.         if saveClip <> nil then
  580.             DisposeRgn(saveClip);
  581.         if controlRgn <> nil then
  582.             DisposeRgn(controlRgn);
  583.         RGBForeColor(saveForeColor);
  584.         RGBBackColor(saveBackColor);
  585.         SetPenState(savePen);
  586.         SetGWorld(savePort, saveDevice);
  587.         HUnlock(Handle(controlDataHdl));
  588.     end;
  589.  
  590.  
  591. {    InitControlData    }
  592. {}
  593. {    Initializes the control private data, filling it with the UPPs, the patterns, ecc.    }
  594. {}
  595. {    Entry:    inControlHdl = handle to current control    }
  596. {            inVarCode = variation code of current control    }
  597.     procedure InitControlData (inControlHdl: ControlHandle;
  598.                                     inVarCode: SInt16);
  599.         var
  600.             theDataHdl: CelsiusCDEFDataHandle;
  601.             drawUPP, blitUPP: DeviceLoopDrawingUPP;
  602.             offWorldPtr: GWorldPtr;
  603.             err: OSErr;
  604.     begin
  605.         theDataHdl := nil;
  606.  
  607.     { Allocate memory for the control's data }
  608.         theDataHdl := CelsiusCDEFDataHandle(NewHandleClear(SizeOf(CelsiusCDEFData)));
  609.         err := MemError;
  610.  
  611.     { If the allocation was successful then fill in the fields, else punt }
  612.         if (err = noErr) and (theDataHdl <> nil) then
  613.             begin
  614.  
  615.         { Lock the data to avoid problems (the following calls should move memory) }
  616.                 HLock(Handle(theDataHdl));
  617.  
  618.         { Store the draw and blit procedures }
  619.                 drawUPP := NewDeviceLoopDrawingProc(@DrawCelsiusControl);
  620.                 theDataHdl^^.fDrawControlUPP := drawUPP;
  621.                 blitUPP := NewDeviceLoopDrawingProc(@BlitCelsiusControl);
  622.                 theDataHdl^^.fBlitControlUPP := blitUPP;
  623.  
  624.         { Store the offscreen graphics world }
  625.                 err := CreateControlOffscreenWorld(inControlHdl, offWorldPtr);
  626.                 if err = noErr then
  627.                     theDataHdl^^.fOffscreenWorldPtr := offWorldPtr;
  628.  
  629.         { Store the patterns; getting them via Resource Mgr. calls helps insulating us from using the evil }
  630.         { QuickDraw globals }
  631.                 GetIndPattern(theDataHdl^^.fBlackPattern, sysPatListID, kBlackPatternIndex);
  632.                 GetIndPattern(theDataHdl^^.fWhitePattern, sysPatListID, kWhitePatternIndex);
  633.                 GetIndPattern(theDataHdl^^.fDitherPattern, sysPatListID, kGrayPatternIndex);
  634.  
  635.         { Store the colors; note that the foreground and background colors of the control's window are stored }
  636.         { by the BeginDraw procedure }
  637.                 SetRGBColor(theDataHdl^^.fBlackColor, kBlackColorRGBComp, kBlackColorRGBComp, kBlackColorRGBComp);
  638.                 SetRGBColor(theDataHdl^^.fWhiteColor, kWhiteColorRGBComp, kWhiteColorRGBComp, kWhiteColorRGBComp);
  639.  
  640.                 if BAND(inVarCode, kUseStdColorsVarCodeMask) <> 0 then
  641.                     begin
  642.                         SetRGBColor(theDataHdl^^.fDarkGrayColor, kDarkGrayColorRGBComp, kDarkGrayColorRGBComp, kDarkGrayColorRGBComp);
  643.                         SetRGBColor(theDataHdl^^.fSteelBlueColor, kSteelBlueColorRGComp, kSteelBlueColorRGComp, kSteelBlueColorBComp);
  644.                         SetRGBColor(theDataHdl^^.fDimGrayColor, kDimGrayColorRGBComp, kDimGrayColorRGBComp, kDimGrayColorRGBComp);
  645.                     end;
  646.  
  647.         { This color is calculated only if the variation code specifies that we should draw the inset effect }
  648.                 if BAND(inVarCode, kInsetBorderVarCodeMask) <> 0 then
  649.                     SetRGBColor(theDataHdl^^.fChiselGrayColor, kChiselGrayColorRGBComp, kChiselGrayColorRGBComp, kChiselGrayColorRGBComp);
  650.  
  651.         { Store the variation code of the current control; unfortunately, we don't have any other way of }
  652.         { passing it to the drawing routine }
  653.                 theDataHdl^^.fVariationCode := inVarCode;
  654.  
  655.         { Store the depth of the control's port }
  656.                 theDataHdl^^.fDepth := GetControlPortDepth(inControlHdl);
  657.  
  658.         { Unlock data }
  659.                 HUnlock(Handle(theDataHdl));
  660.             end;
  661.  
  662.     { Store the initialized data in the contrlData field; note that even the NIL handle of a unsuccessful }
  663.     { allocation is stored, because is checked by the draw routine that exits if encounters such a handle }
  664.         inControlHdl^^.contrlData := Handle(theDataHdl);
  665.     end;
  666.  
  667.  
  668. {    DisposeControlData    }
  669. {}
  670. {    Disposes of all the private data created at initialization time    }
  671. {}
  672. {    Entry:    inControlHdl = handle to control    }
  673.     procedure DisposeControlData (inControlHdl: ControlHandle);
  674.         var
  675.             theDataHdl: CelsiusCDEFDataHandle;
  676.     begin
  677.         theDataHdl := CelsiusCDEFDataHandle(inControlHdl^^.contrlData);
  678.  
  679.     { Go ahead only if our data is not NIL }
  680.         if theDataHdl <> nil then
  681.             begin
  682.  
  683.         { Dispose of the draw and blit UPPs }
  684.                 DisposeRoutineDescriptor(theDataHdl^^.fDrawControlUPP);
  685.                 DisposeRoutineDescriptor(theDataHdl^^.fBlitControlUPP);
  686.  
  687.         { Dispose of the offscreen graphics world }
  688.                 if theDataHdl^^.fOffscreenWorldPtr <> nil then
  689.                     DisposeGWorld(theDataHdl^^.fOffscreenWorldPtr);
  690.  
  691.         { Finally, dispose of the data itself and set it to NIL to avoid multiple disposal }
  692.                 DisposeHandle(Handle(theDataHdl));
  693.                 theDataHdl := nil;
  694.             end;
  695.     end;
  696.  
  697.  
  698. {    Main    }
  699. {}
  700. {    Main entry point for the progress bar control definition function. Dispatches the messages to the    }
  701. {    appropriate subroutines    }
  702. {}
  703. {    Entry:    inVarCode = variation of control to handle    }
  704. {            inControlHdl =    handle to ControlRecord describing the current control    }
  705. {            inMessage = identifies the subfunction requested    }
  706. {            inParam = variable value, depending on inMessage    }
  707. {    Exit:    function result = variable value, depending on inMessage    }
  708.     function Main (inVarCode: SInt16;
  709.                                     inControlHdl: ControlHandle;
  710.                                     inMessage: ControlDefProcMessage;
  711.                                     inParam: SInt32): SInt32;
  712.         var
  713.             returnValue: SInt32;
  714.             ctrlRecState: SignedByte;
  715.     begin
  716.  
  717.     { Don't waste time if we're called with a NIL control handle (this should not happen anyway) }
  718.         if inControlHdl = nil then
  719.             Exit(Main);
  720.  
  721.     { Lock down our control for the whole drawing time }
  722.         ctrlRecState := HGetState(Handle(inControlHdl));
  723.         HLock(Handle(inControlHdl));
  724.  
  725.     { Return 0 as default from our defproc (we don't have indicators) }
  726.         returnValue := 0;
  727.  
  728.     { Dispatch the current message to the appropriate subroutine }
  729.         case inMessage of
  730.  
  731.     { Draw the control (only if it's visible) }
  732.             drawCntl: 
  733.                 if inControlHdl^^.contrlVis <> 0 then
  734.                     BeginDraw(inControlHdl, inVarCode);
  735.  
  736.     { Initializes the control's data }
  737.             initCntl: 
  738.                 InitControlData(inControlHdl, inVarCode);
  739.  
  740.     { Disposes of the control's data }
  741.             dispCntl: 
  742.                 DisposeControlData(inControlHdl);
  743.  
  744.     { Return kInCelsiusControlPart if the click is inside the control's rect }
  745.             testCntl: 
  746.                 if PtInRect(Point(inParam), inControlHdl^^.contrlRect) then
  747.                     returnValue := kInCelsiusControlPart;
  748.  
  749.     { Return the control's rectangle as a region, in 32-bit addressing mode }
  750.             calcCntlRgn: 
  751.                 RectRgn(RgnHandle(inParam), inControlHdl^^.contrlRect);
  752.  
  753.     { Return the control's rectangle as a region, in 24-bit addressing mode; note that IM-Toolbox Essentials }
  754.     { p. 5-112 says that we should clear the high-order bit before calculating the region, but does not specify }
  755.     { that the region handle we return must be confined to the low 3 bytes of inParam }
  756.             calcCRgns: 
  757.                 if BAND(inParam, kClearHighByteMask) = 0 then
  758.                     RectRgn(RgnHandle(StripAddress(inParam)), inControlHdl^^.contrlRect);
  759.  
  760.             otherwise
  761.                 ;
  762.         end;
  763.  
  764.     { Unlock the control and return }
  765.         HSetState(Handle(inControlHdl), ctrlRecState);
  766.         Main := returnValue;
  767.     end;
  768.  
  769.  
  770. end.